#pragma once

// class declarations
class CogObjective;
class CogMultiObjective; 
class CogResultStage;
class CogResult;
class CogSolution;
class CogProduction; // pointer only

#include "CogEvaluator.h"

// ------------------------------------------------------------------------------------------

class CogObjective
{
public:
	CogObjective() : rank(0), pEvaluator(NULL) {}
	CogEvaluator* create(const string& _name, const string& _parameter, uint32_t _rank);
	void destroy() { delete pEvaluator; }

	// data:
	string name;
	string parameter;
	uint32_t rank;
	CogEvaluator* pEvaluator;
};

// ------------------------------------------------------------------------------------------

class CogMultiObjective
{
public:
	void addObjective(const CogObjective& objective);
	void clear();
	size_t stages() const { return table.size(); }
	size_t stageSize(size_t stage) const { return table[stage].size(); }
	size_t objectiveSize() const { size_t objs = 0; for (size_t i = 0; i < table.size(); i++) { objs += table[i].size(); } return objs; }
	const string& objectiveName(size_t stage, size_t index) const { return table[stage][index].name; }
	CogEvaluator* objectiveEvaluator(size_t stage, size_t index) const { return table[stage][index].pEvaluator; }

	void evaluate(CogSolution& solution);
	bool dominates(CogSolution& first, CogSolution& second, size_t stage);

	void toStream(ostream &out);
	
protected:
	vector<vector<CogObjective>> table;
};

// ------------------------------------------------------------------------------------------

bool cogSolutionCompare(const CogSolution& left, const CogSolution& right);
extern bool(*fpSolutionCompare)(const CogSolution&, const CogSolution&);

// -----

class CogResultStage
{
public:
	CogResultStage() : rank(0) {}
	size_t size() const { return values.size(); }
	void resize(size_t wantedSize) { values.resize(wantedSize); }
	double& operator[](size_t index) { return values[index]; }
	const double& operator[](size_t index) const { return values[index]; }
	
	// data:
	size_t rank;

private:
	vector<double> values; // private: use the above methods (less confusing)
};

// -----

class CogResult
{
public:
	size_t size() const { return stages.size(); }
	void resize(size_t wantedSize) { stages.resize(wantedSize); }
	CogResultStage& operator[](size_t index) { return stages[index]; }
	const CogResultStage& operator[](size_t index) const { return stages[index]; }

private:
	// data:
	vector<CogResultStage> stages;
};

// -----

class CogSolution
{
public:
	bool hasPrimaryZero();

	// data:
	CogProduction* pRule;
	CogResult result;
};

// ------------------------------------------------------------------------------------------